package calao.client.util;

import java.util.Date;

import calao.client.i18n.CalendarConstants;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.i18n.client.DateTimeFormat;

public class CalendarDate 
{
	
	public static CalendarConstants CONSTANTS = 
		(CalendarConstants)GWT.create(CalendarConstants.class);
			
	public static final int SUNDAY = 0;
	public static final int MONDAY = 1;
	public static final int TUESDAY = 2;
	public static final int WEDNESDAY = 3;
	public static final int THURSDAY = 4;
	public static final int FRIDAY = 5;
	public static final int SATURDAY = 6;
	
	public static final long MINUTE_MILLIS = 60000;
	public static final long HOUR_MILLIS = MINUTE_MILLIS*60;
	public static final long DAY_MILLIS = HOUR_MILLIS*24;
	
	static int YEAR_MASK =         0xFFFFF000;
	static int MONTH_MASK =        0x00000F00;
	static int DAY_OF_MONTH_MASK = 0x000000FF;

	JavaScriptObject date; 

	int year, month, day, weekDay, hour, minute;
	
	boolean timeSignificant = true;
	
	public CalendarDate()
	{
		setTimeInMillis(System.currentTimeMillis());
	}
	
	public CalendarDate(Date date)
	{
		setTimeInMillis(date.getTime());
	}

	public CalendarDate(Date date, boolean timeSignificant)
	{
		this(date);
		this.timeSignificant = timeSignificant;
	}

	public CalendarDate(long timeMillis)
	{
		setTimeInMillis(timeMillis);
	}
	
	public CalendarDate(long timeMillis, boolean timeSignificant)
	{
		this(timeMillis);
		this.timeSignificant = timeSignificant;
	}

	public CalendarDate(CalendarDate date)
	{
		this.date = date.date;
		this.year = date.year;
		this.month = date.month;
		this.day = date.day;
		this.weekDay = date.weekDay;
		this.hour = date.hour;
		this.minute = date.minute;
		this.timeSignificant = date.timeSignificant;
	}

	public CalendarDate(int year, int month, int day)
	{
		set(year, month, day);
		timeSignificant = false;
	}
	
	public native void set(int year, int month, int day, int hour, int minute) /*-{
		this.@calao.client.util.CalendarDate::date = new Date(year, month, day, hour, minute);
		this.@calao.client.util.CalendarDate::computeFields()();
	}-*/;

	public native void set(int year, int month, int day) /*-{
		this.@calao.client.util.CalendarDate::date = new Date(year, month, day);
    	this.@calao.client.util.CalendarDate::computeFields()();
	}-*/;
	
	public native void setDayTime(int hour, int minute) /*-{
		this.@calao.client.util.CalendarDate::date.setHours(hour, minute);
		this.@calao.client.util.CalendarDate::computeFields()();
	}-*/;

	public native void setTimeInMillis(long millis) /*-{
		var date = new Date();
		date.setTime(millis);
		this.@calao.client.util.CalendarDate::date = date;
		this.@calao.client.util.CalendarDate::computeFields()();
	}-*/;
	
	public native long getTimeInMillis() /*-{
		return this.@calao.client.util.CalendarDate::date.getTime();
	}-*/;

	native void computeFields() /*-{
		this.@calao.client.util.CalendarDate::year = 
			this.@calao.client.util.CalendarDate::date.getFullYear();
		this.@calao.client.util.CalendarDate::month = 
			this.@calao.client.util.CalendarDate::date.getMonth();
		this.@calao.client.util.CalendarDate::day = 
			this.@calao.client.util.CalendarDate::date.getDate();
		this.@calao.client.util.CalendarDate::weekDay = 
			this.@calao.client.util.CalendarDate::date.getDay();
		this.@calao.client.util.CalendarDate::hour = 
			this.@calao.client.util.CalendarDate::date.getHours();
		this.@calao.client.util.CalendarDate::minute = 
			this.@calao.client.util.CalendarDate::date.getMinutes();
	}-*/;

	public void incMonth(int offset)
	{
		if (offset==0) return;
		set(year, month+offset, day, hour, minute);
	}

	public void incYear(int offset)
	{
		if (offset==0) return;
		set(year+offset, month, day, hour, minute);
	}
	
	public void incDay(int offset)
	{
		if (offset==0) return;
		set(year, month, day+offset, hour, minute);
	}
	
	public int countDaysFrom(CalendarDate date)
	{
		return (int)((this.getTimeInMillis()-date.getTimeInMillis())/DAY_MILLIS);
	}

	public int getYear()
	{
		return year;
	}
	
	public int getMonth()
	{
		return month;
	}
	
	public int getDay()
	{
		return day;
	}
	
	public int getHour()
	{
		return hour;
	}

	public int getMinute()
	{
		return minute;
	}

	public void setTime(Date date)
	{
		setTimeInMillis(date.getTime());
	}
	
	public Date getTime()
	{
		return new Date(getTimeInMillis());
	}
	
	public int getWeekDay()
	{
		return weekDay;
	}
	
	public int compareDateTo(CalendarDate cal)
	{
		int compMonth = compareMonthTo(cal);
		if (compMonth!=0) return compMonth;
		if (day<cal.day) return -1;
		if (day>cal.day) return 1;
		return 0;
	}
	
	public int compareTo(CalendarDate cal)
	{
		int compDate = compareDateTo(cal);
		if (compDate!=0 || (!timeSignificant) || (!cal.timeSignificant)) return compDate;
		if (hour<cal.hour) return -1;
		if (hour>cal.hour) return 1;
		if (minute<cal.minute) return -1;
		if (minute>cal.minute) return 1;
		return 0;
	}

	public int compareMonthTo(CalendarDate cal)
	{
		if (year<cal.year) return -1;
		if (year>cal.year) return 1;
		if (month<cal.month) return -1;
		if (month>cal.month) return 1;
		return 0;
	}
	
	public String toString()
	{
		StringBuffer buffer = new StringBuffer();
		appendTo(buffer);
		return buffer.toString();
	}
	
	void appendTo(StringBuffer buffer)
	{
		buffer.append(year);
		buffer.append('-');
		formatTo(month+1, buffer);
		buffer.append('-');
		formatTo(day, buffer);
		if (timeSignificant) {
			buffer.append(' ');
			formatTo(hour, buffer);
			buffer.append(':');
			formatTo(minute, buffer);
		}		
	}
	
	public String formatDayTime()
	{
		StringBuffer buffer = new StringBuffer();
		formatTo(hour, buffer);
		buffer.append(':');
		formatTo(minute, buffer);
		return buffer.toString();
	}
	
	public String formatShortDate()
	{
		return DateTimeFormat.getShortDateFormat().format(getTime());
	}
	
	public String formatFullDate()
	{
		return DateTimeFormat.getFullDateFormat().format(getTime());
	}

	static void formatTo(int number, StringBuffer buffer)
	{
		if (number<10) buffer.append('0');
		buffer.append(number);
	}
	
	public CalendarDate copy()
	{
		return new CalendarDate(getTimeInMillis());
	}
		
	public CalendarDate copyWithOffset(int years, int months, int days)
	{
		CalendarDate result = copy();
		if (years!=0) result.incYear(years);
		if (months!=0) result.incMonth(months);
		if (days!=0) result.incDay(days);
		return result;
	}
	
	public int dateHashCode() 
	{
		return (year*12+month)*31+day;
	}
	
	public int hashCode() 
	{
		return timeSignificant? (dateHashCode()*24+hour)*60+minute : dateHashCode();
	}

	public boolean isTimeSignificant() 
	{
		return timeSignificant;
	}

	public void setTimeSignificant(boolean timeSignificant) 
	{
		this.timeSignificant = timeSignificant;
	}
	
	public boolean equals(Object obj) {
		try {
			return ((CalendarDate)obj).compareTo(this)==0;
		}
		catch (ClassCastException e) {
			return false;
		}
	}
	
	public boolean isFirstDayOfWeek()
	{
		return weekDay==CONSTANTS.firstDayOfWeek();
	}
	
	/** Computes a unique date code for this date.
	 * The returned code does not take time into account.
	 */
	public int getDateCode()
	{
		return (getYear()<<12) | (getMonth()<<8) | getDay();	
	}
	
	/** Gets a date from a date code.
	 * @see #getDateCode()
	 */
	public static CalendarDate getDate(int code)
	{
		return new CalendarDate((code&YEAR_MASK)>>12, (code&MONTH_MASK)>>8,	code&DAY_OF_MONTH_MASK);
	}

}
